export default class DateUtil {
  /**
   * 获取当前日期 YYYY-MM-DD
   * @returns {string} 格式化后的日期字符串
   */
  static getToday(): string {
    return this.formatDate(new Date());
  }

  static getNowTime(): string {
    return this.formatDateTime(new Date());
  }

  /**
   * 将 Date 或 时间字符串格式化为 YYYY-MM-DD
   * @param date 日期对象 或 时间字符串
   * @returns {string} 格式化后的日期字符串
   */
  static formatDate(date: Date | string): string {
    const d = typeof date === "string" ? new Date(date) : date;
    if (isNaN(d.getTime())) {
      throw new Error("Invalid date format");
    }
    return `${d.getFullYear()}-${this.padZero(d.getMonth() + 1)}-${this.padZero(d.getDate())}`;
  }

  /**
   * 将 Date 或 时间字符串格式化为 YYYY-MM-DD HH:mm:ss
   * @param date 日期对象 或 时间字符串
   * @returns {string} 格式化后的日期字符串
   */
  static formatDateTime(date: Date | string): string {
    const d = typeof date === "string" ? new Date(date) : date;
    if (isNaN(d.getTime())) {
      throw new Error("Invalid date format");
    }
    return `${d.getFullYear()}-${this.padZero(d.getMonth() + 1)}-${this.padZero(d.getDate())} ${this.padZero(d.getHours())}:${this.padZero(d.getMinutes())}:${this.padZero(d.getSeconds())}`;
  }

  /**
   * 安全解析日期对象
   * @param input 日期输入（Date对象或ISO字符串）
   * @throws {Error} 当输入无效时抛出错误
   */
  private static safeParseDate(input: Date | string): Date {
    const date = typeof input === 'string' ? new Date(input) : input;

    if (isNaN(date.getTime())) {
      throw new Error(`Invalid date input: ${input}`);
    }

    return date;
  }

  /**
   * 格式化日期时间为自定义格式
   * @param input 日期对象或可解析的日期字符串
   * @param format 格式字符串（支持：YYYY/yy-年，MM-月，DD/dd-日，HH/hh-时，mm-分，ss-秒）
   * @example
   * DateTimeFormatter.formatCustom(new Date(), 'YYYY/MM/DD HH:mm') // "2023/05/15 14:30"
   */
  static formatCustom(input: Date | string, format: string): string {
    const date = this.safeParseDate(input);

    const replacements: Record<string, string> = {
      'YYYY': date.getFullYear().toString(),
      'yy': date.getFullYear().toString().slice(-2),
      'MM': this.padZero(date.getMonth() + 1),
      'DD': this.padZero(date.getDate()),
      'dd': this.padZero(date.getDate()),
      'HH': this.padZero(date.getHours()),
      'hh': this.padZero(date.getHours() % 12 || 12),
      'mm': this.padZero(date.getMinutes()),
      'ss': this.padZero(date.getSeconds())
    };

    return format.replace(
      /YYYY|yy|MM|DD|dd|HH|hh|mm|ss/g,
      match => replacements[match]
    );
  }

  /**
   * 获取指定日期的时间戳
   * @param date 日期对象 或 时间字符串
   * @returns {number} 时间戳（毫秒）
   */
  static getTimestamp(date: Date | string):
    number {
    return new Date(date).getTime();
  }

  /**
   * 计算两个日期的天数差
   * @param startDate 开始日期
   * @param endDate 结束日期
   * @returns {number} 天数差
   */
  static dateDiff(startDate: string | Date, endDate: string | Date): number {
    const start = new Date(startDate).getTime();
    const end = new Date(endDate).getTime();
    return Math.ceil((end - start) / (1000 * 60 * 60 * 24)); // 毫秒转天数
  }

  /**
   * 判断是否是闰年
   * @param year 年份（可选，默认当前年）
   * @returns {boolean} 是否是闰年
   */
  static isLeapYear(year: number = new Date().getFullYear()): boolean {
    return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
  }

  /**
   * 获取指定月份的天数
   * @param year 年份
   * @param month 月份（1-12）
   * @returns {number} 该月的天数
   */
  static getDaysInMonth(year: number, month: number): number {
    return new Date(year, month, 0).getDate();
  }

  /**
   * 补零方法（确保数字是两位数）
   * @param num 数字
   * @returns {string} 补零后的字符串
   */
  private static padZero(num: number): string {
    return num < 10 ? `0${num}` : `${num}`;
  }


  /**
   * 判断第一个日期是否在第二个日期之前
   * @param firstDate 第一个日期
   * @param secondDate 第二个日期
   * @param includeEqual 是否包含相等的情况（默认false）
   * @returns 如果firstDate早于secondDate返回true，否则false
   */
  public static isBefore(firstDate: Date | string | number, secondDate: Date | string | number, includeEqual: boolean = false): boolean {
    // 统一转换为Date对象
    const d1 = this.normalizeDate(firstDate);
    const d2 = this.normalizeDate(secondDate);

    return includeEqual
      ? d1.getTime() <= d2.getTime()
      : d1.getTime() < d2.getTime();
  }

  /**
   * 标准化日期输入（私有方法）
   */
  private static normalizeDate(date: Date | string | number): Date {
    if (date instanceof Date) return date;
    if (typeof date === 'string') return new Date(date);
    if (typeof date === 'number') return new Date(date);
    throw new Error('Invalid date format');
  }

  /**
   * 获取指定日期是星期几
   * @param input 日期对象或可解析的日期字符串
   * @param format 返回格式：'number' | 'short' | 'full' | 'chinese'（默认：'number'）
   * @param timeZone 时区（如：'Asia/Shanghai'），默认使用本地时区
   * @returns
   *   - number: 0-6（0表示周日，1-6表示周一到周六）
   *   - short: "Sun", "Mon", "Tue" 等
   *   - full: "Sunday", "Monday" 等
   *   - chinese: "周日", "周一" 等
   * @throws 当输入日期无效时抛出错误
   *
   * @example
   * getDayOfWeek(new Date()) // 返回 1（如果今天是周一）
   * getDayOfWeek('2023-05-15', 'chinese') // 返回 "周一"
   * getDayOfWeek('2023-05-15', 'full', 'America/New_York') // 返回 "Sunday"（纽约时区）
   */
  public static getDayOfWeek = (
    input: Date | string,
    format: 'number' | 'short' | 'full' | 'chinese' = 'number',
    timeZone?: string
  ): number | string => {
    // 安全解析日期
    const date = typeof input === 'string' ? new Date(input) : input;
    if (isNaN(date.getTime())) throw new Error('Invalid date input');

    // 处理时区（如果指定）
    let utcDate = date;
    if (timeZone) {
      const timeZoneDate = new Date(
        date.toLocaleString('en-US', {timeZone})
      );
      if (!isNaN(timeZoneDate.getTime())) {
        utcDate = timeZoneDate;
      }
    }

    // 获取星期索引（0-6）
    const dayIndex = utcDate.getDay();

    // 根据格式返回结果
    switch (format) {
      case 'number':
        return dayIndex;
      case 'short':
        return ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][dayIndex];
      case 'full':
        return [
          'Sunday',
          'Monday',
          'Tuesday',
          'Wednesday',
          'Thursday',
          'Friday',
          'Saturday',
        ][dayIndex];
      case 'chinese':
        return ['周日', '周一', '周二', '周三', '周四', '周五', '周六'][dayIndex];
      default:
        throw new Error(`Unsupported format: ${format}`);
    }
  }

}

